<script setup lang="ts">

import { onMounted, onUnmounted, type Ref, ref } from 'vue'
import { Rain } from '@/components/rain'

const props = defineProps({
  // 宽
  width: {
    type: Number,
    required: true
  },
  //高
  height: {
    type: Number,
    required: true
  },
  //canvas 样式
  canvasClassName: {
    type: String
  },
  // 背景图片
  backgroundImg: {
    required: true
  },
  //雨滴数量
  rainLen: {
    type: Number,
    default: 50
  },
  //雨滴大小
  rainSize: {
    type: Number,
    default: 18
  },
  //雨滴间距，融合距离
  rainSpace: {
    type: Number,
    default: 3
  }
})

//canvas
const canvasInstance: Ref<HTMLCanvasElement | undefined> = ref()
let canvasCtx: CanvasRenderingContext2D | null = null
const image = new Image()

//雨滴数据
let rainList: Rain[] = []

//生成雨滴
const generatorRain = () => {
  if (rainList.length >= props.rainLen) {
    return
  }
  //如果雨滴数量和规定数量不匹配则生成
  const diff = props.rainLen - rainList.length
  for (let i = 0; i < diff; i++) {
    const x = Math.floor(Math.random() * (props.width + 1))
    const y = Math.floor(Math.random() * (props.height + 1))
    const width = Math.floor(Math.random() * (props.rainSize + 1))
    //保证高度一定大于width
    const height = Math.floor(Math.random() * (width + 1) + width)

    //添加到数组中
    rainList.push(new Rain(x, y, width, height, false))
  }
}

//雨滴融合
const rainFuse = () => {
  //过滤出界的雨滴
  rainList = rainList.filter((val: Rain) => !(val.x >= props.width || val.x <= 0 || val.y > props.height))
  //雨滴融合
  label:for (let i = 0; i < rainList.length; i++) {
    const rai = rainList[i]
    //已经融合过的跳过
    if (rai.dead) continue

    for (let j = i + 1; j < rainList.length; j++) {
      const raj = rainList[j]

      //0 -  10000000000 = -100000000
      //距离差距小于给定间距则可以融合
      if (Math.abs(rai.x - raj.x) < Math.abs(rai.width + raj.width) / 2 + props.rainSpace &&
        Math.abs(rai.y - raj.y) < Math.abs(rai.height + raj.height) / 2 + props.rainSpace) {
        //小于间距设置x,y的值
        if (Math.random() > 0.5) {
          rai.x += props.rainSpace / 2
        } else {
          rai.x -= props.rainSpace / 2
        }
        // 选择最大的雨滴
        rai.y = Math.max(rai.y, raj.y)
        rai.y += props.rainSpace / 2


        //重新设置高宽
        rai.width = Math.max(rai.width, raj.width)
        rai.height = Math.max(rai.height, raj.height)
        rai.width += Math.abs(rai.width - raj.width) / 4
        rai.height += Math.abs(rai.height - raj.height) / 4

        //设置为已融合
        raj.dead = true

        //只能和一个雨滴融合，并不设置它下滑
        continue label
      }
    }

    //下落，一定是 高大于宽
    rai.exchangeWithHeight()

    //设置下落值 受 height 影响
    rai.y += (rai.height / props.rainSize) * 0.2
  }

  //过滤已经被融合的雨滴
  rainList = rainList.filter((val: Rain) => !val.dead)
}

//绘画雨滴
const rainDraw = () => {
  //清除屏幕
  canvasCtx?.clearRect(0, 0, props.width, props.height)
  canvasCtx!.filter = 'blur(10px)'
  //绘制背景图片
  //缩放大小
  const zoom = (props.height + 25) / image.height
  //比例缩放之后图片宽度
  const w = image.width * zoom
  const x = -(Math.abs(w - props.width) / 2)
  canvasCtx?.drawImage(image, -10, 0, props.width, props.height + 25)
  canvasCtx?.drawImage(image, x, 0, w, props.height + 25)
  canvasCtx!.filter = 'none'

  //绘制雨滴
  rainList.forEach((val: Rain) => {
    //保存会话
    canvasCtx?.save()
    canvasCtx!.filter = 'blur(1px)'
    canvasCtx?.beginPath()
    canvasCtx?.ellipse(val.x + val.width / 2, val.y + val.height / 2, val.width / 2, val.height / 2, 0, 0, Math.PI * 2)
    canvasCtx?.closePath()

    //创建蒙版
    canvasCtx?.clip()

    //绘制图片
    canvasCtx?.drawImage(image, val.x, val.y, val.width, val.height)
    //清除会话
    canvasCtx?.restore()
  })
}

//主要动画函数
let animationId = 0
const rain = () => {

  //生成雨滴
  generatorRain()
  //雨滴融合
  rainFuse()
  //渲染
  rainDraw()

  animationId = requestAnimationFrame(rain)

}

onMounted(() => {
  //获取2d上下文
  canvasCtx = canvasInstance.value!.getContext('2d')
  //加载图片
  image.onload = () => {
    //调用渲染
    // setInterval(() => {
    rain()
    // }, 1000 / 60)

  }


  //设置url
  image.src = props.backgroundImg as string
})

//卸载页面之后取消动画
onUnmounted(() => {
  cancelAnimationFrame(animationId)
})

</script>

<template>
  <canvas ref="canvasInstance" :width="width" :height="height" :class="canvasClassName"></canvas>
</template>

